package cn.fintecher.pangolin.service.repair.service;

import cn.fintecher.pangolin.common.enums.ApplyFileContent;
import cn.fintecher.pangolin.common.exception.BadRequestException;
import cn.fintecher.pangolin.common.model.CuscdrFileModel;
import cn.fintecher.pangolin.common.model.RepairFileModel;
import cn.fintecher.pangolin.common.model.UnZipFileModel;
import cn.fintecher.pangolin.common.utils.*;
import cn.fintecher.pangolin.entity.elastic.UploadFile;
import cn.fintecher.pangolin.service.repair.respository.UploadLocalFileRepository;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * Created by ChenChang on 2017/12/26.
 */
@Service("uploadFileService")
public class UploadFileService {
    private final Logger log = LoggerFactory.getLogger(UploadFileService.class);
    @Autowired
    private UploadLocalFileRepository uploadLocalFileRepository;

    @Value("${file.dir}")
    private String fileDir;

    @Value("${file.base-url}")
    private String baseUrl;

    @Value("${file.rule-path}")
    private String rulePath;

    private Snowflake snowflake=new Snowflake((int) Thread.currentThread().getId()%1024);

    public UploadFile findOne(String id) {
        UploadFile uploadFile = uploadLocalFileRepository.findById(id).get();
        //uploadFile.setUrl(baseUrl + "/" + uploadFile.getFileName());
        return uploadFile;
    }

    public File findOneFileById(String id) throws Exception {
        UploadFile uploadFile = findOne(id);
        File file = new File(uploadFile.getLocalPath());
        return file;
    }

    public File findOneFileByName(String fileName) {
        cn.fintecher.pangolin.entity.elastic.UploadFile uploadFile = uploadLocalFileRepository.findByFileName(fileName);
        File file = new File(fileDir + "/" + uploadFile.getFileName());
        return file;
    }

    public UploadFile fileUpload(MultipartFile file, Map<String, String> newRule) throws Exception {
        String filePath = this.getFormatFilePath(rulePath, newRule);
        String originalName = file.getOriginalFilename();
        String extensionName = originalName.substring(originalName.lastIndexOf(".") + 1);
        String fileId = String.valueOf(snowflake.next());
        String fileName = fileId + "." + extensionName;
        saveToDisk(file.getInputStream(), extensionName, fileId, fileDir.concat(filePath));
        return saveUploadFile(fileId, fileName, originalName, extensionName, filePath);

    }

    public UploadFile fileUpload(InputStream inputStream, String originalName, String fileDir, String repair, Map<String, String> newRule) throws Exception {
        String filePath = this.getFormatFilePath(rulePath, newRule);
        String extensionName = originalName.substring(originalName.lastIndexOf(".") + 1);
        String fileId = String.valueOf(snowflake.next());
        String fileName = fileId + "." + extensionName;
        saveToDisk(inputStream, extensionName, fileId, fileDir.concat(filePath));
        return saveUploadFile(fileId, repair.concat(fileName), originalName, extensionName, filePath);
    }

    /**
     * 文件写入硬盘
     *
     * @param inputStream
     * @param extensionName
     * @param fileId
     * @throws IOException
     */
    private void saveToDisk(InputStream inputStream, String extensionName, String fileId, String filePath) throws Exception {
        File outFile;
        OutputStream ot = null;
        try {
            outFile = new File(filePath);
            if (!outFile.exists()) {
                outFile.mkdirs();
            }
            String fileName = fileId + "." + extensionName;
            ot = new FileOutputStream(filePath + "/" + fileName);
            byte[] buffer = new byte[1024];
            int len;
            while ((-1 != (len = inputStream.read(buffer)))) {
                ot.write(buffer, 0, len);
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new Exception("file.saveToDisk.error");
        } finally {
            if (Objects.nonNull(ot)) {
                ot.close();
            }
            if (Objects.nonNull(inputStream)){
                inputStream.close();
            }
        }
    }

    /**
     * 保存文件记录到数据库
     *
     * @param fileId
     * @param fileName
     * @param originalName
     * @param extensionName
     * @param filePath
     * @return
     */
    private UploadFile saveUploadFile(String fileId, String fileName, String originalName, String extensionName, String filePath) throws Exception {
        try {
            UploadFile uploadFile = new UploadFile();
            uploadFile.setOriginalName(originalName);
            uploadFile.setExtensionName(extensionName);
            uploadFile.setFileName(fileName);
            uploadFile.setLocalPath(fileDir.concat(filePath).concat(fileName));
            uploadFile.setId(fileId);
            uploadFile.setUrl(baseUrl + "/" + fileName);
            return uploadLocalFileRepository.save(uploadFile);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new Exception("file.saveRecord.error");
        }
    }

    public RepairFileModel uploadCaseFileReduce(InputStream inputStream, ApplyFileContent type) throws Exception {
        String targetTempFilePath = FileUtils.getTempDirectoryPath().concat(File.separator).concat(File.separator).concat(ShortUUID.generateShortUuid()).concat(File.separator);
        List<String> directoryList;
        try {
            directoryList = UnReduceFile.unZip(inputStream, targetTempFilePath, "UTF-8");
        } catch (IOException e) {
            log.error(e.getMessage(), e);
            return null;
        }
        RepairFileModel repairFileModel = new RepairFileModel();
        if (Objects.nonNull(directoryList)) {
            for (String directoryName : directoryList) {
                File file = FileUtils.getFile(targetTempFilePath, directoryName);
                File[] array = file.listFiles();
                String[] paths = directoryName.split("/");
                String repair = type.name().concat("/").concat(paths[paths.length - 1]);
                for (File f : array) {
                    try {
                        if (f.isDirectory()) {
                            continue;
                        }
                        InputStream in = new FileInputStream(f);
                        if (paths.length > 1) {
                            Map<String, String> newRule = new HashMap<>();
                            newRule.put("{idCard}", paths[paths.length-1]);
                            UploadFile uploadFile = fileUpload(in, paths[paths.length - 1].concat("_").concat(f.getName()), fileDir, "", newRule);
                            cn.fintecher.pangolin.common.model.UploadFile model = new cn.fintecher.pangolin.common.model.UploadFile();
                            BeanUtils.copyPropertiesIgnoreNull(uploadFile,model);
                            repairFileModel.getRepairFile().add(model);
                        } else {
                            UploadFile uploadFile = fileUpload(in, f.getName(), fileDir, "", null);
                            cn.fintecher.pangolin.common.model.UploadFile model = new cn.fintecher.pangolin.common.model.UploadFile();
                            BeanUtils.copyPropertiesIgnoreNull(uploadFile,model);
                            repairFileModel.setExcelFile(model);
                        }
                        in.close();
                    } catch (FileNotFoundException e) {
                        log.error(e.getMessage(), e);
                    } catch (IOException e) {
                        log.error(e.getMessage(), e);
                    }
                }
            }
            try {
                FileUtils.deleteDirectory(FileUtils.getFile(targetTempFilePath));
            } catch (IOException e) {
                log.error(e.getMessage(), e);
            }
        }
        return repairFileModel;
    }

    @Async
    public void downFileFromUrl(CuscdrFileModel fileModel){
        try {
            URL httpurl = new URL(fileModel.getUrl());
            //文件存储到本地服务器
            File localFile=new File(fileDir.concat(fileModel.getFileName()));
            FileUtils.copyURLToFile(httpurl,localFile,10*1000,60*1000);
        } catch (Exception e) {
            log.error("录音文件下载失败：{}",fileModel.getUrl());
            log.error(e.getMessage(), e);
        }
    }

    /**
     * 修复资料上传(压缩)
     * @param unZipFileModel
     * @return
     */
    public RepairFileModel unZipCaseFile(UnZipFileModel unZipFileModel){
        try{
            UploadFile uploadFile = findOne(unZipFileModel.getFileId());
            File file = new File(uploadFile.getLocalPath());
            FileInputStream inputStream = new FileInputStream(file);
            return uploadCaseFileReduce(inputStream, unZipFileModel.getType());
        } catch (Exception e) {
            throw new BadRequestException(null, "fileUpload", Objects.isNull(e.getMessage()) ? "fileUpload.is.fail" : e.getMessage());
        }
    }

    /**
     * 通过文件ID下载文件
     * @param fileId
     * @return
     */
    public byte[] getFileStreamById(String fileId){
        UploadFile uploadFile = findOne(fileId);
        if (Objects.isNull(uploadFile)) {
            throw new BadRequestException(null, "getFileStreamById", "file.is.notExist");
        }
        File file = new File(uploadFile.getLocalPath());
        FileInputStream inputStream = null;
        byte[] buffer = null;
        try {
            inputStream = new FileInputStream(file);
            int b = 0;
            buffer = new byte[1024];
            while (b != -1) {
                b = inputStream.read(buffer);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (Objects.nonNull(inputStream)) {
                    inputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return buffer;
    }

    /**
     * 按照规则替换文件路径
     * @param filePath  文件保存路径
     * @param newRule   可变规则
     * @return
     */
    public String getFormatFilePath(String filePath, Map<String, String> newRule){
        String path = filePath;
        //替换可变规则
        if(newRule!=null){
            for(String key:newRule.keySet()){
                if(path.indexOf(key)>-1){
                    path = path.replace(key, newRule.get(key));
                }
            }
        }

        //替换默认规则
        String[] defaultRule = {"{data}","{datetime}","{currentTime}"};
        for(String rule:defaultRule){
            if(path.indexOf(rule)>-1){
                String replacement;
                if(rule.equals("{data}")){
                    replacement = ZWDateUtil.getFormatNowDate("YYYYMMdd");
                }else if(rule.equals("{datetime}")){
                    replacement = ZWDateUtil.getFormatNowDate("YYYYMMddHHmmss");
                }else{
                    replacement = System.currentTimeMillis()+"";
                }
                path = path.replace(rule, replacement);
            }
        }
        int start = path.indexOf("{");
        int end = path.lastIndexOf("}");
        if(start>-1 && end>-1){
            path = path.replace(path.substring(start-1, end+2),"/");
        }
        return path;
    }

}
